package org.simpatia.service.jvm.monitoring;

import org.simpatia.Context;
import org.simpatia.Module;
import org.simpatia.Service;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.io.IOException;
import java.lang.management.*;
import java.text.SimpleDateFormat;
import java.util.*;

public class JVMMonitoringServiceImpl implements
        Service<JVMMonitoringConfiguration, JVMMonitoringService>, JVMMonitoringService {

    private static final String JMX_URL_TEMPLATE = "service:jmx:rmi:///jndi/rmi://%s/jmxrmi";
    private static final Map<MBeanServerConnection, Set<String>> majorsNames = new HashMap<MBeanServerConnection, Set<String>>();

    private Map<String, MBeanServerConnection> connections = new HashMap<String, MBeanServerConnection>();
    private Map<String, JMXConnector> connectors = new HashMap<String, JMXConnector>();

    public void activate(String name, JVMMonitoringConfiguration configuration, Context context) {
        List<String> jmxServiceAddresses = configuration.getJmxAddresses();

        majorsNames.clear();
        for (String jmxServiceAddress : jmxServiceAddresses) {
            try {
                JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(String.format(JMX_URL_TEMPLATE, jmxServiceAddress)));
                connectors.put(jmxServiceAddress, connector);
                MBeanServerConnection mBeanServerConnection = connector.getMBeanServerConnection();
                connections.put(jmxServiceAddress, mBeanServerConnection);
                ObjectName memPoolName = new ObjectName(ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE + ",*");

                Set<ObjectName> srvMem = mBeanServerConnection.queryNames(memPoolName, null);
                for (ObjectName memMgr : srvMem) {
                    MemoryPoolMXBean memoryPoolMXBean = ManagementFactory.newPlatformMXBeanProxy(mBeanServerConnection,
                            memMgr.toString(), MemoryPoolMXBean.class);
                    try {
                        if (MemoryType.NON_HEAP.equals(memoryPoolMXBean.getType()))
                            if (!majorsNames.containsKey(mBeanServerConnection)) {
                                majorsNames.put(mBeanServerConnection, new HashSet<String>(Arrays.asList(memoryPoolMXBean.getMemoryManagerNames())));
                            } else {
                                majorsNames.get(mBeanServerConnection).addAll( new HashSet<String>(Arrays.asList(memoryPoolMXBean.getMemoryManagerNames())));
                            }
                    } catch (UnsupportedOperationException e) { }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        if (connections.size() == 0) {
            throw new RuntimeException("Error during JMX initialization. ZERO connections created.");
        }
    }

    public void destroy() {
        for(JMXConnector connector : connectors.values()) {
            try {
                connector.close();
            } catch (IOException e) {
                //log
            }
        }
    }

    public JVMMonitoringService getRESTInterface() {
        return this;
    }

    @GET
    @Produces("application/json")
    @Path("/poll")
    public List<JVMMetrics> getJVMMetrics() {
        List<JVMMetrics> result = new ArrayList<JVMMetrics>();
        for(String address : connections.keySet()) {
            MBeanServerConnection connection = connections.get(address);

            JVMMetrics statistics = new JVMMetrics();
            result.add(statistics);

            statistics.setAddress(address);

            try {
                MemoryMXBean memoryMXBean = ManagementFactory.newPlatformMXBeanProxy(connection,
                        ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class);
                MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();

               statistics.getNumericalParameters().put("HEAP_MEMORY_USAGE_INIT", (double)heapMemoryUsage.getInit());

            } catch (Exception e) {
                //log
            }
        }

        return result;
    }

    public void registerService(String serviceName, Module module) {
    }

    public void unregisterService(String serviceName) {
    }
}
